Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Outputing subset messages as received (initial work) #233

Merged
merged 15 commits into from
Sep 20, 2018

Conversation

DemiMarie
Copy link
Contributor

Closes #170.

This is still incomplete; in particular, I need to fix up the test suite.

src/subset.rs Outdated
if self.decided || self.count_true() < self.netinfo.num_correct() {
return None;
}
// Once all instances of BA have completed, let C ⊂ [1..N] be
// the indexes of each BA that delivered 1. Wait for the output
// v_j for each RBC_j such that j∈C. Finally output ∪ j∈C v_j.
if self.ba_results.len() < self.netinfo.num_nodes() {
return None;
return Some(SubsetOutput::Contribution(proposer_id.clone(),
self.broadcast_results.get(proposer_id).map(|x|x.clone()).expect("internal error: already inserted entry not found")));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rustfmt won't accept this in Travis. Please check the error message.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vkomenda Fixed

tests/subset.rs Outdated Show resolved Hide resolved
src/subset.rs Outdated Show resolved Hide resolved
@DemiMarie
Copy link
Contributor Author

I made no attempt to avoid unnecessary calls to .clone(). I figured that it was more important to get the functionality working.

Copy link
Contributor Author

@DemiMarie DemiMarie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Current version: all tests pass

src/honey_badger/epoch_state.rs Outdated Show resolved Hide resolved
src/subset.rs Outdated Show resolved Hide resolved
This outputs subset messages as they are received.  All tests pass.
@DemiMarie
Copy link
Contributor Author

Squashed.

Copy link
Collaborator

@afck afck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work! Thanks for taking on this issue, @DemiMarie!
I highlighted a few points that need to be addressed before we can merge it.

src/honey_badger/epoch_state.rs Outdated Show resolved Hide resolved
let mut map: BTreeMap<N, Vec<u8>> = Default::default();
self.keys.insert(k.clone());
map.insert(k, v);
//step.extend(self.send_decryption_shares(map)?);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think we should do that here: it's the optimization that will hopefully speed up Honey Badger.

We should probably also change send_decryption_share[s] to take only one value and get rid of map.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So when I do that, I keep running into:

--- test_dynamic_honey_badger_random_delivery_silent stdout ----
thread 'test_dynamic_honey_badger_random_delivery_silent' panicked at 'no more messages in queue', libcore/option.rs:989:5
note: Run with `RUST_BACKTRACE=1` for a backtrace

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@afck is this a testsuite bug or a bug in my code?

src/subset.rs Outdated
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SubsetOutput<N> {
Contribution(N, Vec<u8>),
Done(BTreeMap<N, Vec<u8>>),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to include the values here again? The user has already received the values so it would suffice to just return the set of accepted node IDs (N), or even nothing at all.

Since the values can be large, it would be nice to avoid any unnecessary cloning.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. I think we need the set of accepted node IDs (so that we can determine whether we are done), but we don’t need to return it.

src/subset.rs Outdated Show resolved Hide resolved
src/subset.rs Outdated Show resolved Hide resolved
src/subset.rs Outdated Show resolved Hide resolved
tests/subset.rs Outdated
assert_eq!(inputs[&id], value);
}
match output {
SubsetOutput::Contribution(_, _) => unreachable!(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test probably needs to be updated: The algorithm doesn't make the guarantee that all nodes receive the Contributions in the same order. What we need to verify is that:

  • Each node outputs the same Contributions, although possibly in a different oder. (No duplicates allowed.)
  • Each node outputs Done after all the Contributions, and no further outputs after Done.
  • The values in the Contributions are the ones matching the corresponding input (already checked below).

src/subset.rs Outdated
&mut self,
proposer_id: &N,
result: Option<Vec<u8>>,
) -> Option<SubsetOutput<N>> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the "true" count check in this method should be moved further down, at least if we now want to output Contributions (but not Done) even before we have num_correct() instances that returned true.

In any case, we need to make sure that for each accepted value (where the agreement instance returns true), we output Contribution exactly once. Note that this can be triggered by two things:

  • An agreement instance outputs true and we already have the value—the output of the broadcast instance—for it.
  • A broadcast instance outputs a value and we already have received the output true from the corresponding agreement instance. (Not very likely, but it can happen if our node is lagging.)

This fixes the test suite, while still outputting results early.
There is a testsuite failure in the `dynamic_honey_badger` tests.  Is
this a testsuite bug?
src/subset.rs Outdated Show resolved Hide resolved
tests/subset.rs Outdated Show resolved Hide resolved
src/subset.rs Outdated Show resolved Hide resolved
src/subset.rs Outdated Show resolved Hide resolved
src/honey_badger/epoch_state.rs Outdated Show resolved Hide resolved
src/honey_badger/epoch_state.rs Outdated Show resolved Hide resolved
src/subset.rs Outdated Show resolved Hide resolved
tests/subset.rs Outdated Show resolved Hide resolved
@DemiMarie
Copy link
Contributor Author

DemiMarie commented Sep 17, 2018

@DemiMarie
Copy link
Contributor Author

@afck what should I change to fix the test suite?

@c0gent
Copy link
Contributor

c0gent commented Sep 17, 2018

@DemiMarie The most recent error is simply a formatting error. Just run cargo +beta fmt and it should clear up.

Copy link
Collaborator

@afck afck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we check both rustfmt and Clippy (currently disabled, I think) in CI.

src/honey_badger/epoch_state.rs Outdated Show resolved Hide resolved
src/honey_badger/epoch_state.rs Outdated Show resolved Hide resolved
src/honey_badger/epoch_state.rs Outdated Show resolved Hide resolved
src/honey_badger/epoch_state.rs Outdated Show resolved Hide resolved
src/honey_badger/epoch_state.rs Outdated Show resolved Hide resolved
src/honey_badger/epoch_state.rs Outdated Show resolved Hide resolved
src/subset.rs Show resolved Hide resolved
src/subset.rs Outdated Show resolved Hide resolved
tests/subset.rs Outdated Show resolved Hide resolved
tests/subset.rs Outdated Show resolved Hide resolved
tests/subset.rs Outdated Show resolved Hide resolved
@DemiMarie
Copy link
Contributor Author

DemiMarie commented Sep 18, 2018

Is it normal that the testsuite seems to run in an infinite loop?

Edit: Obviously not ― can someone help debug the root cause?

Copy link
Collaborator

@afck afck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At a glance I couldn't spot the reason for the failing tests so far, but I'm pretty sure something is still wrong with the map field, since it seems to be read but never written.

If the tests are hanging that probably means that the algorithm currently never terminates but keeps sending messages.

Is it the Honey Badger or the Subset test (or both?) that's hanging?

You can run a single test with the environment variable RUST_LOG=hbbft=debug to see all the spam very helpful logs. Often it's easier to just use hbbft=info and add specific info! logs in the code for debugging to find out what exactly is going on.

src/subset.rs Outdated Show resolved Hide resolved
src/subset.rs Outdated Show resolved Hide resolved
src/subset.rs Outdated Show resolved Hide resolved
src/honey_badger/epoch_state.rs Outdated Show resolved Hide resolved
Otherwise, we reject all nodes as faulty.
@DemiMarie
Copy link
Contributor Author

All tests pass on my system.

@DemiMarie
Copy link
Contributor Author

@afck Thank you for your suggestion; the logs allowed me to figure out the problem quickly

There is no need to log a quadratic amount of data.
@DemiMarie
Copy link
Contributor Author

Should I squash?

@vkomenda
Copy link
Contributor

We can squash-merge in github.

Copy link
Contributor

@vkomenda vkomenda left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work! My congratulations on making this work. I have a few minor remarks. Given those are taken into account, I'm for merging this into master. I don't think the change changes censorship resilience. Maybe @afck and @c0gent have different opinion? In any case, the new feature works and it looks good.

tests/subset.rs Show resolved Hide resolved
src/honey_badger/epoch_state.rs Outdated Show resolved Hide resolved
src/honey_badger/epoch_state.rs Outdated Show resolved Hide resolved
the rest of the nodes.  Also `panic!` if `Done` is ever not the last
value in a series of `SubsetOutput`s.
Copy link
Collaborator

@afck afck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Just a few more minor nit-picks.

The tests time out sometimes (we're working on it); I restarted them.

src/honey_badger/epoch_state.rs Outdated Show resolved Hide resolved
src/honey_badger/epoch_state.rs Outdated Show resolved Hide resolved
src/subset.rs Outdated Show resolved Hide resolved
tests/subset.rs Outdated Show resolved Hide resolved
@DemiMarie
Copy link
Contributor Author

@vkomenda @afck is this version good?

tests/subset.rs Show resolved Hide resolved
tests/subset.rs Show resolved Hide resolved
@DemiMarie
Copy link
Contributor Author

DemiMarie commented Sep 19, 2018 via email

@vkomenda
Copy link
Contributor

A BTreeSet would collapse multiple occurrences of the same output to one while a sorted Vec would keep those occurrences. So a sorted Vec represents a multiset - a set with multiplicities of its elements - rather than a simple set, and is more expressive.

Copy link
Collaborator

@afck afck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me! From my side, this can be merged. 👍
(I added one more minor suggestion but that's just a matter of taste.)

@@ -220,7 +227,22 @@ impl<N: NodeIdT + Rand> Subset<N> {
return Ok(step);
}
};
self.broadcast_results.insert(proposer_id.clone(), value);

let val_to_insert = if let Some(true) = self.ba_results.get(proposer_id) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to pattern-match here: This could just be if Some(true) == self.ba_results.get(proposer_id).

On the other hand, maybe it should be a match instead: I'm wondering whether in the Some(false) case, we should also just insert None, since we won't need the value anyway—we already know it has been rejected?

@afck afck merged commit 679f578 into poanetwork:master Sep 20, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Optimization: output early from Subset?
4 participants